import CryptoJS from 'crypto-js'

import input from '../utils/input'
import { reader, writer, checkDir } from '../utils/io'
import checker from '../utils/checker'
import { info, success } from '../utils/output'
import { staticPath, userinfoPath } from '../utils/path'
import { isValidInput, isValidUsername, isValidPassword, isValidUserInfo } from '../utils/validator'

import QuestionModel from '../model/question.model'
import LocationModel from '../model/location.model'
import UserinfoModel from '../model/userinfo.model'

type UserinfoList = Array<UserinfoModel>
type UserinfoKeys = keyof UserinfoModel

const { enc: { Utf8 }, Rabbit: { encrypt, decrypt } } = CryptoJS

const key = '&#!.))*:|l}o' // Secret Key
let regularSignIn: boolean = false // 是否常规登记
let userinfoList: UserinfoList = [] // 全局变量：用户信息

// 账号密码
const acctPwdQuestion: Array<QuestionModel> = [
  {
    name: 'username',
    type: 'input',
    message: '请输入12位学号：',
    validate(val: string) {
      return isValidUsername(val) || '你这学号有问题啊'
    }
  },
  {
    name: 'userpwd',
    type: 'password',
    message: '请输入你的ISP密码：',
    mask: true,
    validate(val: string) {
      return isValidPassword(val) || '你这密码保真吗？怎么六位都不到啊'
    }
  }
]

// 地址
const locationQuestion: Array<QuestionModel> = [
  {
    name: 'province',
    type: 'input',
    message: '请输入你所在的省：',
    validate(val: string) {
      return isValidInput(val) || '还没输入呢'
    }
  },
  {
    name: 'city',
    type: 'input',
    message: '请输入你所在的市：',
    mask: true,
    validate(val: string) {
      return isValidInput(val) || '还没输入呢'
    }
  },
  {
    name: 'area',
    type: 'input',
    message: '请输入你所在的区/县：',
    mask: true,
    validate(val: string) {
      return isValidInput(val) || '还没输入呢'
    }
  }
]

const completeQuestion = [...acctPwdQuestion, ...locationQuestion]

// 加密用户数据
const encryptUserinfo = (data: UserinfoList): UserinfoList => {

  info('加密用户数据...')
  const encryptData: UserinfoList = []
  for (const { ...item } of data) {
    for (const [k, v] of Object.entries(item)) {
      item[(k as UserinfoKeys)] = encrypt(v.toString(), key).toString()
    }
    encryptData.push(item)
  }
  return encryptData
}

// 解密用户数据
const decryptUserinfo = (data: UserinfoList): UserinfoList => {
  info('解密用户数据...')
  const decryptData: UserinfoList = []
  for (const { ...item } of data) {
    for (const [k, v] of Object.entries(item)) {
      item[(k as UserinfoKeys)] = decrypt(v, key).toString(Utf8)
    }
    decryptData.push(item)
  }
  return decryptData
}

const addUserinfo = async (): Promise<void> => {
  let flag: boolean = false
  do {
    const userinfo = await questionQuery(completeQuestion) as UserinfoModel
    userinfoList.push(userinfo)
    flag = await alternativeInquirer('是否继续添加用户')
  } while (flag)
  await saveUserinfo(userinfoList)
}

// Yes or No 问题
const alternativeInquirer = async (message: string): Promise<boolean> => {
  const { answer } = await input([
    {
      message,
      name: 'answer',
      type: 'rawlist',
      choices: [
        { name: '是', value: true },
        { name: '否', value: false }
      ],
      default: 0
    }  
  ])
  return answer as boolean
}

// 保存加密后的用户信息到文件
const saveUserinfo = async ([...data]: UserinfoList): Promise<void> => {
  info('即将在 static/userinfo.json 创建文件保存信息...')
  const encryptData = encryptUserinfo(data)
  await checkDir(staticPath)
  await writer(userinfoPath, JSON.stringify(encryptData))
}

// 根据问题列表获取信息
const questionQuery = async (
  questionList: Array<QuestionModel>
): Promise<LocationModel | UserinfoModel> => {
  const res = await input(questionList)
  return res
}

// 询问是否更改信息 并保存文件
const needRewriteUserinfo = async (
  oldUserinfo: UserinfoModel,
  includeLocation: boolean = true
): Promise<UserinfoModel> => {
  const message = includeLocation ? '需要重新录入信息吗?' : '需要重新输入账号密码吗?'
  const questionList = includeLocation ? completeQuestion : acctPwdQuestion
  const answer = await alternativeInquirer(message)
  checker(answer, '', '用户信息不完整')
  // 获取询问的结果
  const newUserinfo = await questionQuery(questionList)
  // 合并对象
  const userinfo = { ...oldUserinfo, ...newUserinfo } as UserinfoModel
  // 查找该对象在数组中的下标
  const index = userinfoList.findIndex(item => item.username === userinfo.username)
  // 更新对象
  userinfoList.splice(index, 1, userinfo)
  // 保存文件
  await saveUserinfo(userinfoList)
  // 返回更新后的对象供调用者使用
  return userinfo
}

// 验证帐号密码和地址信息，对一些情况做了处理
const checkUserinfo = async (): Promise<[boolean, UserinfoList]> => {
  info('验证文件信息...')
  const [fileNotExists, res] = await reader(userinfoPath)
  if (fileNotExists) {
    // 文件不存在则从控制台读取输入，并写入文件
    regularSignIn = true
    info('文件不存在，第一次运行需要输入以下信息')
    await addUserinfo()
  } else {
    // 存在则解密文件
    const decryptData = decryptUserinfo(res)
    userinfoList.push(...decryptData)
  }
  const addUser: boolean = await alternativeInquirer('是否添加用户')
  if (addUser) {
    const userinfo = await questionQuery(completeQuestion) as UserinfoModel
    userinfoList.push(userinfo)
    saveUserinfo(userinfoList)
  }
  userinfoList.forEach((item, index) => {
    checker(isValidUserInfo(item), '', `第${index + 1}位用户的信息应包含的字段不完整`)
  })
  success('用户信息验证完成')
  return [true, userinfoList] // 风控，暂时使用常规登记
}

export { checkUserinfo, needRewriteUserinfo }
